<template>
  <div class="approval-process" :style="showAnimation">
    <div class="back"></div>
    <div class="front">
      <div class="box">
        <div class="header">
          <div class="title">标题</div>
          <div class="close-btn" @click="hide">
            <Icon type="ios-close" />
          </div>
        </div>
        <div class="main">
          <div class="approval-layout">
            <!-- 左侧审批流程图区域 (40%) -->
            <div class="flow-chart-section">
              <div class="section-header">
                <span class="wy-icon wy-guize"></span>
                <h3>审批流程图</h3>
              </div>
              <div class="flow-chart-content">
                <!-- 流程图区域先空着，后续填充 -->
                <c-flowchart ref="graph" :config="stepsBpmnCf"></c-flowchart>
              </div>
            </div>

            <!-- 右侧详情区域 (60%) -->
            <div class="details-section">
              <!-- 节点详情区域 (50%) -->
              <div class="node-details">
                <div class="section-header">
                  <span class="wy-icon wy-fapiao3"></span>
                  <h3>节点详情</h3>
                </div>
                <div class="card-content">
                  <div v-show="isSelectedNode" class="node-info-card">
                    <div class="node-info-status">
                      <div class="node-status-title">节点状态</div>
                      <div class="node-status-view">
                        <div
                          class="node-status-label"
                          :style="{ backgroundColor: nodeInfo.status.bgColor }"
                        >
                          {{ nodeInfo.status.text }}
                        </div>
                        <div class="node-operation-time">
                          {{ nodeInfo.time }}
                        </div>
                      </div>
                    </div>
                    <div class="info-card-item">
                      <div class="item-title">
                        {{ $srv.getI18nBySrv(nodeInfo.type.label) }}
                      </div>
                      <div class="item-content">
                        <div class="node-type">
                          <div class="node-type-icon">
                            <span
                              :class="['wy-icon', nodeInfo.type.icon]"
                            ></span>
                          </div>
                        </div>
                        <div class="node-info">
                          <div
                            class="node-info-operator"
                            :title="nodeInfo.type.value"
                          >
                            {{ nodeInfo.type.value }}
                          </div>
                          <div
                            v-show="true"
                            class="node-info-operator-content"
                            :title="nodeInfo.type.extra"
                          >
                            {{ nodeInfo.type.extra }}
                          </div>
                        </div>
                      </div>
                    </div>
                    <div class="info-card-item">
                      <div class="item-title">审批意见</div>
                      <div class="item-content">
                        {{ nodeInfo.content }}
                      </div>
                    </div>
                  </div>
                  <div v-show="!isSelectedNode" class="node-info-card">
                    <div class="node-info-empty">
                      {{ $srv.getI18nByValue('无节点数据') }}
                    </div>
                  </div>
                </div>
              </div>

              <!-- 历史记录区域 (50%) -->
              <div class="history-records">
                <div class="section-header">
                  <span class="wy-icon wy-clock"></span>
                  <h3>审批历史</h3>
                </div>
                <div class="card-content">
                  <div class="history-list">
                    <div
                      class="history-item"
                      v-for="history of approvalRecords"
                      :key="history.id"
                    >
                      <div class="history-header">
                        <span class="action-type">{{
                          $srv.getI18nBySrv(history.action.label)
                        }}</span>
                        <span class="action-time">{{ history.time }}</span>
                      </div>
                      <div class="history-detail">
                        <span class="detail-title">操作人：</span>
                        <span class="detail-value">{{
                          history.operator.label
                        }}</span>
                      </div>
                      <div class="history-detail">
                        <span class="detail-title">备注：</span>
                        <span class="detail-value">{{ history.content }}</span>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { N_cFlowchart } from '@/types';
import { I_cmd, T_cmd, T_srv, Tsrv, ifs, stepsBpmnConfig, vue } from './config';
import { N_oApprovalProcess } from './types';

@T_srv.comp({
  components: {}
})
export default class oApprovalProcess extends vue {
  /* 父组件compBoxInfo */
  @T_srv.prop({
    required: false,
    type: Object,
    default: null
  })
  compBoxInfo: ifs.I_compBoxInfo;

  @T_srv.prop() config: N_oApprovalProcess.config;

  @T_srv.prop() id: string;

  /* 组件名 */
  private name: string = 'oApprovalProcess';

  private funName: string = '';

  private G_compName: string = this.name;

  /* 组件箱信息对象 */
  private GT_compBoxInfo: ifs.I_compBoxInfo;

  /* 组件箱对象 */
  private GT_compBox: ifs.I_compBox;

  /* 组件箱识别ID */
  private G_compID: string;

  /* 指挥部 */
  private GT_command: I_cmd = new T_cmd();

  private isShow: boolean = false;

  private showAnimation: any = {};

  //  审核流程条目数据
  public bpmnData: any = null;

  //  审核流程图——图数据
  public graphData: any = null;

  private stepsBpmnCf: N_cFlowchart.I_conf = null;

  // 画布实例
  // private graph: N_cFlowchart.cFlowchart = null;
  private graph: any = null;

  // 内部联络单明细 row数据 （DSL数据）
  private nodesData: Array<any> = [];

  // 具备审批条件的节点， 即：正在进行审批的节点
  private permittedNodes: Array<any> = [];

  // 被选中的节点
  private selectedNode: N_oApprovalProcess.I_node = null;

  // 操作记录
  private approvalRecords: Array<N_oApprovalProcess.I_approvalRecord> = [];

  // 节点信息数据
  private nodeInfo: N_oApprovalProcess.T_nodeInfo = new N_oApprovalProcess.T_nodeInfo();

  // 信息提示
  private infotip: any = {
    node: {
      empty: Tsrv.getI18nByValue('无节点数据'),
      void: Tsrv.getI18nByValue('无效节点数据')
    }
  };

  get isSelectedNode() {
    return !Tsrv.noValue(this.selectedNode);
  }

  created() {
    this.GT_compBoxInfo = Tsrv.getCompInfo(this.compBoxInfo);
    this.G_compID = this.GT_compBoxInfo.compID;
    this.GT_compBox = this.GT_compBoxInfo.TcompBox;
    this.GT_command.init(this, this.GT_compBoxInfo);
    this.verifyConfig();
    this.init();
  }

  mounted() {
    /* 向父组件发送实例 */
    this.GT_compBox.addCompMergeName(this.G_compID, this.G_compName, this);
    this.$emit('compBoxInfo', this.GT_compBoxInfo);
    this.show();
  }

  private init() {
    this.funName = this.name; // 当前配置中的功能名
    const stepsBpmnCf = stepsBpmnConfig();
    stepsBpmnCf.regEvent = this.GT_command.regGraphEvents;
    this.stepsBpmnCf = stepsBpmnCf;
    this.getLiaisonInfo();
  }

  private setSelectedNode(nodeID: string) {
    const nodesData = this.nodesData;
    if (!Tsrv.noValue(nodesData)) {
      const findKey = nodesData.findIndex(node => node.f_code === nodeID);
      if (findKey < 0) {
        this.selectedNode = null;
        return;
      }
      const node = nodesData[findKey];
      const selectedNode: N_oApprovalProcess.I_node = {
        rowID: node?.id,
        nodeID: node?.f_code,
        // 0：审批人、1：条件判断、2：用户组
        type: node?.f_type,
        order: node?.sn,
        content: node?.content ?? '',
        // 0；未执行、1；已执行
        confirm: node?.confirm,
        approver: node?.approver,
        data: node?.graph_data,
        time: node?.last_modify_time ?? node?.create_time ?? ''
      };
      this.selectedNode = selectedNode;
      this.setNodeInfo();
    }
  }

  private setNodeInfo() {
    // nodeInfo
    const maxSn = this.permittedNodes[0].sn;
    const selectedNode = this.selectedNode;
    this.nodeInfo
      .clearData()
      .setTime(selectedNode.time)
      .setContent(selectedNode.content);

    let nodeValue = '';
    if (selectedNode.type === 0) {
      nodeValue = selectedNode.data?.checker?.label;
    }
    if (selectedNode.type === 1) {
      nodeValue = selectedNode.data?.condition?.value;
    }
    if (selectedNode.type === 2) {
      nodeValue = selectedNode.data?.label;
    }
    this.nodeInfo.setType(selectedNode.type, nodeValue, selectedNode.approver);
    if (selectedNode.confirm === 1) {
      // 已审批
      this.nodeInfo.setStatus(2);
    } else if (selectedNode.confirm === 0) {
      if (selectedNode.order === maxSn) {
        // 审批中
        this.nodeInfo.setStatus(1);
      } else {
        // 未审批
        this.nodeInfo.setStatus(0);
      }
    }
  }

  private getLiaisonInfo() {
    const config = this.config.config;
    // 根据config内部联络单的row信息————得到对应bpmn的row数据等到图数据
    Promise.all([
      this.GT_command.getLiaisonDet(config.liaisonID),
      this.GT_command.getLiaisonLog(config.liaisonID),
      this.GT_command.getBpmnData(config.bpmnID)
    ]).then(responses => {
      this.handleLiaisonDetRes(responses[0]);
      this.handleLiaisonLogRes(responses[1]);
      this.handleBpmnRes(responses[2]);
    });
  }

  /**
   * @description: （1、获取所有审批节点数据，2、找到当前审批人数据）
   * @return {*}
   * @author: swc
   * @Date: 2024-11-21 10:29:33
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  private handleLiaisonDetRes(response: any) {
    const [b, res, rows, msg] = response;
    if (!b) {
      msg && this.$msg(msg);
    }
    if (rows.length !== 0) {
      rows.forEach(item => {
        if (Tsrv.noValue(item?.approver)) {
          item.approver = [];
        } else {
          item.approver = JSON.parse(item.approver);
        }
        item.father_data = JSON.parse(item.father_data);
        item.son_data = JSON.parse(item.son_data);
        item.graph_data = JSON.parse(item.graph_data);
      });
    }
    this.nodesData = rows;
    this.GT_command.setNodesData(rows);
    this.getPermittedNodes(rows);
    // 判断当前节点 能否有条件 手动选择 下级节点
    // 若有则找出来
    // this.getSelectableNodes();
  }

  private handleLiaisonLogRes(response: any) {
    const [b, res, rows, msg] = response;
    if (!b) {
      msg && this.$msg(msg);
    }
    if (rows.length > 0) {
      const approvalRecords = [];
      rows.forEach(item => {
        const record = {
          id: item.id,
          action: {
            label: item['slc|f_type|547326363187564544'],
            value: item?.f_type
          },
          operator: {
            label: item['m2o|createor|_sys_user|user_name'],
            value: item?.createor
          },
          content: item?.f_comment ?? '',
          time: item?.create_time
        };
        approvalRecords.push(record);
      });
      this.approvalRecords = approvalRecords;
    }
  }

  private handleBpmnRes(response: any) {
    const [b, res, rows, msg] = response;
    if (!b) {
      msg && this.$msg(msg);
    }
    if (rows.length > 0) {
      this.bpmnData = rows[0];
      const unzipResp = wUnZipStr(rows[0].graph_data);
      if (unzipResp.isErr()) {
        throw new Error(unzipResp.getErrMsg());
      }
      this.graphData = unzipResp.getReturnData('data');
      this.initGraph();
      setTimeout(() => {
        this.handleNodeStyle();
      }, 200);
    }
  }

  private initGraph() {
    if (!Tsrv.noValue(this.graphData)) {
      this.graph = this.$refs.graph;
      let graphData = this.graphData as string;
      if (!Tsrv.utils.isJSONString(graphData)) {
        graphData = JSON.stringify(graphData);
      }
      this.graph.reLoadDataFromJson(graphData);
      this.GT_command.setGraph(this.graph.graph);
      setTimeout(() => {
        this.graph.zoomToFit({
          padding: 20,
          preserveAspectRatio: true
        });
      }, 0);
    }
  }

  private getPermittedNodes(nodesData: Array<any>) {
    let maxSn = nodesData[0].sn;
    for (let i = 1; i < nodesData.length; i++) {
      if (nodesData[i].sn > maxSn) {
        maxSn = nodesData[i].sn;
      }
    }
    const permittedNodes = [];
    for (let i = 0; i < nodesData.length; i++) {
      if (nodesData[i].sn === maxSn) {
        permittedNodes.push(nodesData[i]);
      }
    }
    this.permittedNodes = permittedNodes;
  }

  /**
   * @description: 根据审批节点信息 渲染 审批节点样式
   * @param {*}
   * @return {*}
   * @author: swc
   * @Date: 2024-11-21 10:11:18
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  private handleNodeStyle() {
    const graph: any = this.$refs.graph;
    const x6Graph: N_cFlowchart.x6Graph = graph.graph;
    const nodes = x6Graph.getNodes();
    const nodesData: Array<any> = this.nodesData;
    nodes.forEach(node => {
      // const data = node.getData();
      const nodeProp = node.getProp();
      if (nodeProp.shape === 'bpmn-begin' || nodeProp.shape === 'bpmn-end') {
        // node.updateAttrs({
        //   body: {
        //     fill: '#57a3f3',
        //     stroke: '#000',
        //     strokeWidth: 1,
        //     rx: 26,
        //     ry: 26
        //   }
        // });
        node.setAttrByPath('body/fill', '#57a3f3');
      } else {
        //
        const nodeData = nodesData.find(
          // item => item.f_code === nodeProp.id
          item => item.f_code === nodeProp.data.id
        );
        if (nodeData?.confirm) {
          // node.updateAttrs({
          //   body: {
          //     fill: '#c5c8cef7',
          //     stroke: '#000',
          //     strokeWidth: 1,
          //     rx: 26,
          //     ry: 26
          //   }
          // });
          node.setAttrByPath('body/fill', '#c5c8cef7');
        }
        // if (nodeData?.id === this.curApprover?.id && this.canHandle) {
        //   // node.updateAttrs({
        //   //   body: {
        //   //     fill: '#4bff2d',
        //   //     stroke: '#000',
        //   //     strokeWidth: 1,
        //   //     rx: 26,
        //   //     ry: 26
        //   //   }
        //   // });
        //   node.setAttrByPath('body/fill', '#4bff2d');
        // }
        if (!nodeData?.confirm && nodeProp.shape !== 'bpmn-gateway') {
          if (this.permittedNodes.some(el => el.f_code === node.id)) {
            node.setAttrByPath('body/fill', '#4bff2d');
          }
          // node.setAttrByPath('body/fill', '#4bff2d');
        }
      }
    });
  }

  private verifyConfig() {
    const config = this.config.config;
    if (Tsrv.noValue(config)) {
      throw new Error(Tsrv.getI18nByValue('传入参数不合法，config为空'));
    }
    if (Tsrv.noValue(config.liaisonID)) {
      throw new Error(
        Tsrv.getI18nByValue('传入参数不合法，config.liaisonID为空')
      );
    }
    if (Tsrv.noValue(config.funID)) {
      throw new Error(Tsrv.getI18nByValue('传入参数不合法，config.funID为空'));
    }
    if (Tsrv.noValue(config.bpmnID)) {
      throw new Error(Tsrv.getI18nByValue('传入参数不合法，config.bpmnID为空'));
    }
    if (Tsrv.noValue(config.viewedData)) {
      throw new Error(
        Tsrv.getI18nByValue('传入参数不合法，config.viewedData为空')
      );
    }
  }

  public show() {
    const that = this;
    this.showAnimation = {
      display: 'block',
      // 'z-index': 102 + this.id
      'z-index': 202 + this.id
    };
    setTimeout(() => {
      that.showAnimation = {
        display: 'block',
        opacity: 1,
        // 'z-index': 102 + this.id
        'z-index': 202 + this.id
      };
    }, 10);
  }

  public hide() {
    const that = this;
    this.showAnimation = {
      display: 'block',
      opacity: 0,
      // 'z-index': 102 + this.id
      'z-index': 202 + this.id
    };
    setTimeout(() => {
      that.showAnimation = {
        display: 'none',
        // 'z-index': 102 + this.id
        'z-index': 202 + this.id
      };
      this.$bus.$emit(`modal_${this.id}`);
    }, 200);
  }

  destroyed() {
    /* 注册组件销毁 */
    this.GT_compBox.removeCompMergeName(this.G_compID, this.G_compName);

    /* 如果父组件传入的compBoxInfo则删除本文件产生的引用,反之是本组件产生的ID统一删除 */
    if (Tsrv.utils.isEmpty(this.compBoxInfo)) {
      this.GT_compBox.removeCompsWithCompID(this.G_compID);
    } else {
      this.GT_compBox.removeCompsMergeName(this.G_compID, [
        `'${this.G_compName}'`
      ]);
    }
  }
}
</script>
<style lang="scss" scoped>
.approval-process {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  opacity: 0;
  display: none;
  transition: ease-in-out 0.3s;
  box-sizing: border-box;

  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }
  .back {
    background-color: rgba(0, 0, 0, 0.5);
    width: 100%;
    height: 100%;
  }
  .front {
    position: absolute;
    width: calc(100% - 30px);
    height: calc(100% - 30px);
    top: 15px;
    left: 15px;
    .box {
      width: 100%;
      height: 100%;
      background-color: #fff;
      display: flex;
      flex-direction: column;
      border-radius: var(--border-radius-model);
      overflow: hidden;
      border-radius: 5px;

      .header {
        width: 100%;
        position: relative;
        height: 58px;
        background-color: rgba(244, 244, 244, 1);
        padding: 0 30px;
        box-shadow: var(--default-box-shadow-down);
        z-index: 11;
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        .title {
          font-style: 14px;
        }
        .close-btn {
          font-size: 30px;
          &:hover {
            cursor: pointer;
          }
        }
      }
      .main {
        width: 100%;
        height: calc(100% - 58px); /* 减去header高度 */
        display: flex;
        flex-direction: column;
        background-color: #fff;
        border-radius: var(--border-radius-model);
        overflow: hidden;
        min-height: 0; /* 防止flex项目溢出 */

        .approval-layout {
          width: 100%;
          height: 100%;
          padding: 20px;
          display: grid;
          grid-template-columns: 2fr 3fr;
          grid-template-rows: 1fr;
          gap: 15px;
          overflow: hidden;
          min-height: 0;

          .flow-chart-section {
            display: flex;
            flex-direction: column;
            background: #f8f9fa;
            border-radius: 8px;
            border: 1px solid #e9ecef;
            overflow: hidden;
            min-height: 0;

            .section-header {
              flex: 0 0 auto;
              padding: 16px;
              background: #e9ecef;
              border-bottom: 1px solid #dee2e6;
              display: flex;
              flex-direction: row;
              gap: 5px;

              h3 {
                margin: 0;
                font-size: 16px;
                font-weight: 600;
                color: #495057;
              }
            }

            .flow-chart-content {
              flex: 1;
              display: flex;
              align-items: center;
              justify-content: center;
              padding: 20px;
              min-height: 0;
              overflow: hidden;

              .placeholder-text {
                color: #6c757d;
                font-style: italic;
              }
            }
          }

          .details-section {
            flex: 0 0 60%;
            // display: flex;
            // flex-direction: row;
            display: grid;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr;
            gap: 10px;
            min-height: 0;
            overflow: hidden;

            .node-details {
              flex: 0 0 50%;
              display: flex;
              flex-direction: column;
              background: #f8f9fa;
              border-radius: 8px;
              border: 1px solid #e9ecef;
              overflow: hidden;
              min-height: 0;

              .section-header {
                flex: 0 0 auto;
                padding: 16px;
                background: #e9ecef;
                border-bottom: 1px solid #dee2e6;
                display: flex;
                flex-direction: row;
                gap: 5px;

                h3 {
                  margin: 0;
                  font-size: 16px;
                  font-weight: 600;
                  color: #495057;
                }
              }

              .card-content {
                flex: 1;
                padding: 16px;
                overflow-y: auto;
                min-height: 0;
              }
            }

            .history-records {
              flex: 0 0 50%;
              display: flex;
              flex-direction: column;
              background: #f8f9fa;
              border-radius: 8px;
              border: 1px solid #e9ecef;
              overflow: hidden;
              min-height: 0;

              .section-header {
                flex: 0 0 auto;
                padding: 16px;
                background: #e9ecef;
                border-bottom: 1px solid #dee2e6;
                display: flex;
                flex-direction: row;
                gap: 5px;

                h3 {
                  margin: 0;
                  font-size: 16px;
                  font-weight: 600;
                  color: #495057;
                }
              }

              .card-content {
                flex: 1;
                padding: 16px;
                overflow-y: auto;
                min-height: 0;
              }
            }

            .node-details {
              .node-info-card {
                background: white;
                border-radius: 8px;
                padding: 24px;
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
                border: 1px solid #e9ecef;

                .node-info-status {
                  display: flex;
                  flex-direction: column;
                  gap: 5px;
                  margin-bottom: 20px;

                  .node-status-title {
                    font-weight: bold;
                    font-size: 14px;
                    margin-bottom: 10px;
                  }

                  .node-status-view {
                    display: flex;
                    flex-direction: row;
                    justify-content: space-between;
                    align-items: center;

                    .node-status-label {
                      width: 60px;
                      height: 25px;
                      border-radius: 15px;
                      background-color: skyblue;
                      display: flex;
                      justify-content: center;
                      align-items: center;
                      font-size: 10px;
                      font-weight: bold;
                    }

                    .node-operation-time {
                      font-size: 12px;
                      color: #cccc;
                    }
                  }
                }

                .info-card-item {
                  display: flex;
                  flex-direction: column;
                  margin-bottom: 20px;

                  .item-title {
                    font-weight: bold;
                    font-size: 14px;
                    margin-bottom: 10px;
                  }
                  .item-content {
                    background: #f8f9fa;
                    border-radius: 6px;
                    padding: 16px;
                    // border-left: 4px solid #28a745;
                    display: flex;
                    flex-direction: row;
                    justify-content: start;
                    align-items: center;
                    gap: 10px;

                    .node-type {
                      display: flex;
                      flex-direction: row;
                      justify-content: center;
                      align-items: center;
                      height: 30px;
                      width: 30px;
                      border-radius: 15px;
                      background-color: #2d8eff;
                      color: #fff;

                      .node-type-icon {
                        display: flex;
                        justify-content: center;
                        align-items: center;
                      }
                    }

                    .node-info {
                      display: flex;
                      flex-direction: column;
                      gap: 3px;
                      overflow: hidden;

                      .node-info-operator {
                        font-weight: bold;
                      }

                      .node-info-operator-content {
                        font-size: 12px;
                        color: #ccc;
                      }
                    }
                  }
                }

                .node-info-empty {
                  display: flex;
                  justify-content: center;
                  align-items: center;
                  font-weight: 700;
                  font-size: 13px;
                }
              }
            }

            .history-records {
              .history-list {
                .history-item {
                  background: white;
                  border-radius: 6px;
                  padding: 16px;
                  margin-bottom: 12px;
                  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
                  border-left: 4px solid #007bff;

                  &:last-child {
                    margin-bottom: 0;
                  }

                  .history-header {
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    margin-bottom: 8px;

                    .action-type {
                      font-weight: 600;
                      color: #495057;
                    }

                    .action-time {
                      font-size: 12px;
                      color: #6c757d;
                    }
                  }

                  .history-detail {
                    display: flex;
                    align-items: center;
                    // gap: 8px;
                    margin-bottom: 6px;

                    .detail-title {
                      font-weight: 500;
                      // color: #007bff;
                    }

                    .detail-value {
                      // color: #6c757d;
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
</style>
